import os
import sys
import asyncio
import platform
import logging
import subprocess
import re
from jinja2 import Template
from pyppeteer import launch

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout),
        logging.FileHandler('../html_to_pdf.log', encoding='utf-8')
    ]
)
logger = logging.getLogger(__name__)


def check_system_fonts():
    """检查系统中可用的中文字体"""
    system_type = platform.system().lower()
    available_fonts = []

    if system_type == 'linux':
        try:
            # 检查常见的中文字体
            result = subprocess.run(['fc-list', ':lang=zh'],
                                    capture_output=True, text=True, timeout=10)
            if result.returncode == 0:
                font_lines = result.stdout.strip().split('\n')
                logger.info(f"系统中找到 {len(font_lines)} 个中文字体")

                # 提取字体族名称
                for line in font_lines:
                    if ':' in line and line.strip():
                        try:
                            # 更准确地解析字体名称
                            parts = line.split(':')
                            if len(parts) >= 2:
                                font_path = parts[0].strip()
                                font_info = parts[1].strip()

                                # 提取字体族名称 (去除样式信息)
                                if ',' in font_info:
                                    font_family = font_info.split(',')[0].strip()
                                else:
                                    font_family = font_info.strip()

                                # 清理字体名称
                                font_family = font_family.replace('\\', '').strip()

                                if font_family and font_family not in available_fonts:
                                    available_fonts.append(font_family)
                                    if len(available_fonts) <= 10:  # 只显示前10个
                                        logger.info(f"  - {font_family}")
                        except Exception as e:
                            logger.debug(f"解析字体行失败: {line} - {e}")

                # 如果没有找到字体，尝试检查所有字体
                if not available_fonts:
                    logger.info("未找到中文字体，检查所有系统字体...")
                    result_all = subprocess.run(['fc-list'],
                                                capture_output=True, text=True, timeout=10)
                    if result_all.returncode == 0:
                        all_fonts = result_all.stdout.strip().split('\n')
                        logger.info(f"系统总共有 {len(all_fonts)} 个字体")

                        # 查找可能的中文字体
                        chinese_keywords = ['CJK', 'Han', 'Hei', 'Song', 'Ming', 'WenQuanYi', 'Noto', 'Source', 'Droid']
                        for line in all_fonts[:20]:  # 只检查前20个
                            for keyword in chinese_keywords:
                                if keyword.lower() in line.lower():
                                    try:
                                        parts = line.split(':')
                                        if len(parts) >= 2:
                                            font_family = parts[1].split(',')[0].strip()
                                            if font_family and font_family not in available_fonts:
                                                available_fonts.append(font_family)
                                                logger.info(f"  - {font_family} (通过关键词匹配)")
                                    except:
                                        pass
                                    break
            else:
                logger.warning("无法执行 fc-list 命令，可能未安装 fontconfig")
        except Exception as e:
            logger.warning(f"字体检测失败: {e}")

    logger.info(f"总共检测到 {len(available_fonts)} 个可用中文字体")
    return available_fonts


def get_font_css(system_type, available_fonts=None):
    """根据系统类型和可用字体生成CSS字体样式"""
    if system_type == 'linux':
        # Linux系统的字体优先级列表 - 更全面的列表
        linux_fonts = [
            # Google Noto 系列 (推荐)
            "Noto Sans CJK SC",
            "Noto Sans SC",
            "Noto Sans CJK",
            "Noto Color Emoji",

            # Adobe 思源系列
            "Source Han Sans SC",
            "Source Han Sans CN",
            "Source Han Sans",

            # 文泉驿系列
            "WenQuanYi Micro Hei",
            "WenQuanYi Zen Hei",
            "WenQuanYi Bitmap Song",

            # 其他常见中文字体
            "Droid Sans Fallback",
            "AR PL UMing CN",
            "AR PL UKai CN",
            "UnDotum",

            # 传统中文字体
            "SimHei",
            "SimSun",
            "FangSong",
            "KaiTi",

            # 系统默认字体
            "DejaVu Sans",
            "Liberation Sans",
            "Ubuntu",
            "Arial Unicode MS",
            "sans-serif"
        ]

        # 如果检测到可用字体，优先使用，但保持原有字体作为fallback
        if available_fonts:
            # 去重合并，保持检测到的字体在前面
            font_list = []
            for font in available_fonts:
                if font not in font_list:
                    font_list.append(font)
            for font in linux_fonts:
                if font not in font_list:
                    font_list.append(font)
        else:
            font_list = linux_fonts

    else:
        # 其他系统（Windows, macOS）
        font_list = [
            "Microsoft YaHei",
            "SimHei",
            "SimSun",
            "PingFang SC",
            "Helvetica Neue",
            "Arial Unicode MS",
            "sans-serif"
        ]

    # 构建字体族字符串
    font_family_str = ", ".join([f'"{font}"' for font in font_list])

    css_content = f'''
    /* 导入网络字体作为备用 */
    @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap');

    /* 全局字体设置 */
    * {{
        font-family: {font_family_str} !important;
        -webkit-font-smoothing: antialiased !important;
        -moz-osx-font-smoothing: grayscale !important;
        text-rendering: optimizeLegibility !important;
    }}

    /* 主要元素字体设置 */
    html, body {{
        font-family: {font_family_str} !important;
        font-size: 14px !important;
        line-height: 1.6 !important;
    }}

    body, p, div, span, h1, h2, h3, h4, h5, h6, td, th, li, a, label, input, textarea, select {{
        font-family: {font_family_str} !important;
        font-weight: normal !important;
    }}

    /* 标题元素特殊处理 */
    h1, h2, h3, h4, h5, h6 {{
        font-family: {font_family_str} !important;
        font-weight: bold !important;
    }}

    /* 表格元素 */
    table, thead, tbody, tr, td, th {{
        font-family: {font_family_str} !important;
    }}

    /* 特别处理中文字符 */
    :lang(zh), :lang(zh-CN), :lang(zh-TW), [lang="zh"], [lang="zh-CN"], [lang="zh-TW"] {{
        font-family: {font_family_str} !important;
    }}

    /* 强制覆盖可能的内联样式 */
    [style*="font-family"] {{
        font-family: {font_family_str} !important;
    }}

    /* 确保文本渲染质量 */
    body {{
        font-feature-settings: "kern" 1 !important;
        font-variant-ligatures: common-ligatures !important;
        -webkit-text-size-adjust: 100% !important;
        -moz-text-size-adjust: 100% !important;
        text-size-adjust: 100% !important;
    }}
    '''

    return css_content


async def html_to_pdf_async(html_filename, pdf_filename=None):
    """
    将HTML文件转换为PDF文件（异步版本）

    Args:
        html_filename (str): 输入的HTML文件名
        pdf_filename (str, optional): 输出的PDF文件名，默认为HTML文件名替换扩展名

    Returns:
        bool: 转换成功返回True，失败返回False
    """
    browser = None
    try:
        if not os.path.exists(html_filename):
            logger.error(f"HTML文件不存在: {html_filename}")
            return False

        with open(html_filename, 'r', encoding='utf-8') as f:
            html_content = f.read()

        # 检测HTML中是否使用了Chart.js和D3.js
        uses_chartjs = 'chart.js' in html_content.lower() or 'chart.min.js' in html_content
        uses_d3js = 'd3.js' in html_content.lower() or 'd3.v7.min.js' in html_content.lower()

        logger.info("检测到的库:")
        logger.info(f"  Chart.js: {'是' if uses_chartjs else '否'}")
        logger.info(f"  D3.js: {'是' if uses_d3js else '否'}")

        # 使用正则表达式替换所有以 https:// 开头的 chart.js 相关 URL
        if uses_chartjs:
            # 匹配所有以 https:// 开头且包含 chart.js 或 chart.min.js 的 URL
            chart_pattern = r'https://[^"\s]*chart(?:\.min)?\.js[^"\s]*'
            html_content = re.sub(chart_pattern, './static/js/chart.min.js', html_content)
            logger.info("已将所有 Chart.js CDN 链接替换为本地路径")

        if uses_d3js:
            # 匹配所有以 https:// 开头且以 d3.v7.min.js 或 d3.v7.js 结尾的 URL
            d3_pattern = r'https://[^"\s]*d3\.v7(?:\.min)?\.js[^"\s]*'
            html_content = re.sub(d3_pattern, './static/js/d3.v7.min.js', html_content)
            logger.info("已将所有 D3.js CDN 链接替换为本地路径")

        template = Template(html_content)
        rendered_html = template.render()

        temp_html = 'temp_rendered.html'
        with open(temp_html, 'w', encoding='utf-8') as f:
            f.write(rendered_html)

        logger.info(f"HTML 文件已生成：{temp_html}")

        if pdf_filename is None:
            pdf_filename = os.path.splitext(html_filename)[0] + '.pdf'

        # 检测操作系统并设置浏览器启动参数
        system_type = platform.system().lower()
        logger.info(f"检测到操作系统: {system_type}")

        # 检查系统字体
        available_fonts = check_system_fonts()

        browser_args = [
            '--no-sandbox',
            '--disable-dev-shm-usage',
            '--allow-file-access-from-files',
            '--disable-web-security',
            '--disable-background-timer-throttling',
            '--disable-backgrounding-occluded-windows',
            '--disable-renderer-backgrounding',
            '--lang=zh-CN',
            '--disable-font-subpixel-positioning',  # 禁用字体子像素定位
            '--enable-font-antialiasing',  # 启用字体抗锯齿
            '--force-device-scale-factor=1'  # 强制设备缩放因子为1
        ]

        # Linux 特殊配置
        if system_type == 'linux':
            browser_args.extend([
                '--disable-gpu',
                '--disable-software-rasterizer',
                '--disable-background-timer-throttling',
                '--disable-backgrounding-occluded-windows',
                '--disable-renderer-backgrounding',
                '--run-all-compositor-stages-before-draw',
                '--disable-new-content-rendering-timeout'
            ])

        launch_options = {
            'headless': True,
            'args': browser_args,
            'ignoreHTTPSErrors': True,
            'dumpio': False,
            'handleSIGINT': False,
            'handleSIGTERM': False,
            'handleSIGHUP': False
        }

        chrome_executable = get_chrome_executable()
        if chrome_executable:
            launch_options['executablePath'] = chrome_executable
            logger.info(f"使用系统Chrome: {chrome_executable}")
        else:
            logger.info(f"{system_type}环境，使用默认Chrome路径")

        # 启动浏览器
        logger.info("正在启动浏览器...")
        browser = await launch(**launch_options)
        page = await browser.newPage()

        # 设置视口大小
        await page.setViewport({'width': 1200, 'height': 800})

        # 监听控制台消息和错误
        page.on('console', lambda msg: logger.debug(f"浏览器控制台: {msg.text}"))
        page.on('pageerror', lambda err: logger.error(f"页面错误: {err}"))

        # 加载HTML文件
        file_url = f'file://{os.path.abspath(temp_html)}'
        logger.info(f"正在加载页面: {file_url}")
        await page.goto(file_url, {'waitUntil': 'networkidle0', 'timeout': 30000})

        # 注入优化的中文字体样式
        logger.info("注入优化的中文字体样式...")
        font_css = get_font_css(system_type, available_fonts)
        await page.addStyleTag({'content': font_css})

        # 设置页面语言和字符集
        await page.evaluate('''
            document.documentElement.lang = "zh-CN";
            document.documentElement.setAttribute("xml:lang", "zh-CN");

            // 设置字符集
            var metaCharset = document.createElement('meta');
            metaCharset.setAttribute('charset', 'utf-8');
            document.head.insertBefore(metaCharset, document.head.firstChild);
        ''')

        # 注入额外的字体处理JavaScript
        await page.evaluate('''
            // 强制所有文本元素使用中文字体
            function applyChineseFonts() {
                var elements = document.querySelectorAll('*');
                var fontFamily = getComputedStyle(document.body).fontFamily;

                for (var i = 0; i < elements.length; i++) {
                    var el = elements[i];
                    if (el.textContent && el.textContent.trim()) {
                        // 检查是否包含中文字符
                        var hasChinese = /[\\u4e00-\\u9fff]/.test(el.textContent);
                        if (hasChinese || true) { // 对所有元素都应用
                            el.style.fontFamily = fontFamily;
                            el.style.setProperty('font-family', fontFamily, 'important');
                        }
                    }
                }
            }

            // 立即执行一次
            applyChineseFonts();

            // 观察DOM变化，确保动态内容也能正确显示
            var observer = new MutationObserver(applyChineseFonts);
            observer.observe(document.body, {
                childList: true,
                subtree: true,
                characterData: true
            });

            // 页面加载完成后再执行一次
            if (document.readyState === 'complete') {
                applyChineseFonts();
            } else {
                document.addEventListener('DOMContentLoaded', applyChineseFonts);
                window.addEventListener('load', applyChineseFonts);
            }
        ''')

        # 根据检测结果等待相应的库加载
        if uses_chartjs:
            logger.info("等待Chart.js库加载...")
            try:
                await page.waitForFunction('typeof Chart !== "undefined"', {'timeout': 15000})
                logger.info("✓ Chart.js库已加载")
            except Exception as e:
                logger.warning(f"✗ Chart.js库加载超时: {e}")

        if uses_d3js:
            logger.info("等待D3.js库加载...")
            try:
                await page.waitForFunction('typeof d3 !== "undefined"', {'timeout': 15000})
                logger.info("✓ D3.js库已加载")
            except Exception as e:
                logger.warning(f"✗ D3.js库加载超时: {e}")

        # 如果使用了任何图表库，额外等待渲染时间
        if uses_chartjs or uses_d3js:
            logger.info("等待图表渲染...")
            await asyncio.sleep(5)

            # 等待可能的图表容器元素
            try:
                await page.waitForSelector('canvas, svg, .chart-container, #chart', {'timeout': 10000})
                logger.info("✓ 图表元素已找到")
            except Exception as e:
                logger.warning(f"图表元素检测超时: {e}")
        else:
            logger.info("未检测到图表库，跳过额外等待")
            await asyncio.sleep(2)

        # 强制重新渲染页面以确保字体加载
        logger.info("强制重新渲染页面...")
        await page.evaluate('''
            (function() {
                // 强制重新计算样式
                document.body.style.display = 'none';
                document.body.offsetHeight; // 触发重排
                document.body.style.display = 'block';

                // 等待字体加载 - 使用 Promise 方式
                if (document.fonts && document.fonts.ready) {
                    document.fonts.ready.then(function() {
                        console.log('字体加载完成');
                    });
                }

                // 再次应用字体
                var elements = document.querySelectorAll('*');
                var bodyFont = getComputedStyle(document.body).fontFamily;
                for (var i = 0; i < elements.length; i++) {
                    var el = elements[i];
                    if (el.textContent && el.textContent.trim()) {
                        el.style.setProperty('font-family', bodyFont, 'important');
                    }
                }

                return true;
            })();
        ''')

        # 等待字体完全加载并应用
        logger.info("等待字体完全加载...")
        await asyncio.sleep(5)

        # 检查字体是否正确应用
        font_check_result = await page.evaluate('''
            (function() {
                var body = document.body;
                var computedFont = getComputedStyle(body).fontFamily;
                var sampleText = document.querySelector('h1, h2, h3, p, div, span');
                var appliedFont = 'unknown';

                if (sampleText) {
                    appliedFont = getComputedStyle(sampleText).fontFamily;
                }

                var hasChineseText = /[\\u4e00-\\u9fff]/.test(document.body.textContent);

                return {
                    bodyFont: computedFont,
                    sampleFont: appliedFont,
                    hasChineseText: hasChineseText
                };
            })();
        ''')

        logger.info(f"字体检查结果: {font_check_result}")

        # 调试截图
        debug_path = os.path.join('test', 'debug_page.png')
        # 确保test目录存在
        os.makedirs('test', exist_ok=True)
        await page.screenshot({'path': debug_path})
        logger.info(f"调试截图已保存: {debug_path}")

        # 生成PDF
        logger.info("正在生成PDF...")
        await page.pdf({
            'path': pdf_filename,
            'format': 'A4',
            'printBackground': True,
            'preferCSSPageSize': False,  # 不使用CSS页面大小
            'margin': {
                'top': '20px',
                'bottom': '20px',
                'left': '20px',
                'right': '20px'
            }
        })

        logger.info(f"PDF文件已生成：{pdf_filename}")
        return True

    except Exception as e:
        logger.error(f"转换失败：{str(e)}", exc_info=True)
        return False
    finally:
        # 确保浏览器被正确关闭
        if browser:
            try:
                await browser.close()
                logger.info("浏览器已关闭")
            except Exception as e:
                logger.warning(f"关闭浏览器时出错: {e}")

        # 清理临时文件
        temp_html = 'temp_rendered.html'
        if os.path.exists(temp_html):
            try:
                os.remove(temp_html)
                logger.debug(f"已删除临时文件: {temp_html}")
            except Exception as e:
                logger.warning(f"删除临时文件失败: {e}")


def get_chrome_executable():
    """获取Chrome可执行文件路径"""
    system_type = platform.system().lower()

    if system_type == 'darwin':  # macOS
        possible_paths = [
            '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
            '/Applications/Chromium.app/Contents/MacOS/Chromium'
        ]
    elif system_type == 'linux':
        possible_paths = [
            '/usr/bin/google-chrome',
            '/usr/bin/chromium-browser',
            '/usr/bin/chromium',
            '/root/.local/share/pyppeteer/local-chromium/588429/chrome-linux_588429/chrome'
        ]
    else:  # Windows
        possible_paths = [
            'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
            'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
        ]

    for path in possible_paths:
        if os.path.exists(path):
            return path

    return None


def html_to_pdf(html_filename, pdf_filename=None):
    """
    将HTML文件转换为PDF文件（同步包装器）
    """
    # 创建新的事件循环，避免在非主线程中的问题
    try:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        result = loop.run_until_complete(html_to_pdf_async(html_filename, pdf_filename))
        return result
    except Exception as e:
        logger.error(f"同步包装器执行失败: {e}")
        return False
    finally:
        try:
            loop.close()
        except Exception:
            pass


def main():
    """主函数，处理命令行参数"""
    if len(sys.argv) < 2:
        logger.info("用法: python html_to_pdf.py <html_filename> [pdf_filename]")
        logger.info("示例: python html_to_pdf.py charts.html")
        logger.info("示例: python html_to_pdf.py charts.html output.pdf")
        return

    html_filename = sys.argv[1]
    pdf_filename = sys.argv[2] if len(sys.argv) > 2 else None

    success = html_to_pdf(html_filename, pdf_filename)

    if success:
        logger.info("转换完成！")
    else:
        logger.error("转换失败！")
        sys.exit(1)


if __name__ == "__main__":
    main()